﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Reflection;
using fastJSON;
using Less.Core;

namespace Less.Tools
{
    public static class Functions
    {
        public static object fileToken = new object();
        private const int BIN10 = 1024;

        /// <summary>
        /// Go thru the stack to get the calling method name.
        /// </summary>
        /// <returns></returns>
        public static string GetCallingMethodName(int steps = 2)
        {
            string callingMethod = String.Empty;
            var stack = new StackTrace(steps);
            StackFrame frame = stack.GetFrame(0);
            if (frame != null)
            {
                MethodBase method = frame.GetMethod();
                if (method != null)
                {
                    callingMethod = GetFriendlyName(method);
                    callingMethod = ShortString(callingMethod);
                }
            }
            return callingMethod;
        }

        /// <summary>
        /// Go thru the stack to get the calling method name.
        /// </summary>
        /// <returns></returns>
        public static MethodBase GetCallingMethod(int steps = 2)
        {
            MethodBase callingMethod = null;
            var stack = new StackTrace(steps);
            StackFrame frame = stack.GetFrame(0);
            if (frame != null)
                callingMethod = frame.GetMethod();
            return callingMethod;
        }

        public static string GetFriendlyName(MethodBase method)
        {
            if (method == null)
                throw new ArgumentNullException("method");
            return method.DeclaringType + "." + method.Name;
        }

        /// <summary>
        /// Format to get less verbose logs
        /// </summary>
        /// <param name="me"></param>
        /// <returns></returns>
        public static string ShortString(string me)
        {
            if (me.HasValue())
            {
                string shortName = me.Trim();
                shortName = shortName.Replace("System.", "S.");
                shortName = shortName.Replace("Data.", "D.");
                shortName = shortName.Replace("SqlServer", "Sql");
                shortName = shortName.Replace("Connection", "Cnn");
                shortName = shortName.Replace("Command", "Cmd");
                shortName = shortName.Replace("Database", "Db");
                shortName = shortName.Replace("Exception", "Excp");
                shortName = shortName.Replace("Execute", "Exct");
                shortName = shortName.Replace("Framework", "Frmw");
                shortName = shortName.Replace("Less", "Ls");
                return shortName;
            }
            return String.Empty;
        }

        public static string CleanMessage(string msg)
        {
            return
            msg.Replace("'", "").Replace("\"", "").Replace(Environment.NewLine, " -- ").Replace("\r", " -- ").
            Replace("\n", " -- ");
        }

        public static string ReadText(string filePath)
        {
            if (filePath == null)
                throw new ArgumentNullException("filePath");
            string textReaded = String.Empty;
            if (File.Exists(filePath))
            {
                lock (fileToken)
                {
                    using (var stream = new StreamReader(filePath))
                    {
                        textReaded += stream.ReadToEnd();
                        stream.Close();
                    }
                }
            }
            else
                Log.Warning(Msg.PathNotFound(filePath));
            return textReaded;
        }

        public static void WriteText(string filePath, string text)
        {
            if (filePath == null)
                throw new ArgumentNullException("filePath");
            string directory = Path.GetDirectoryName(filePath);
            VerifyDirectory(directory);
            lock (fileToken)
            {
                using (var file = new StreamWriter(filePath))
                {
                    file.WriteLine(text);
                    file.Close();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="useSerializerExtension">Allows deserializin by reflection, but generates bigger files</param>
        /// <returns></returns>
        public static string Serialize(object instance, bool useSerializerExtension = true)
        {
            if (instance == null)
                throw new ArgumentNullException("instance");
            string serializedString = null;
            JSON.Instance.UseSerializerExtension = useSerializerExtension;
            serializedString = JSON.Instance.ToJSON(instance);
            return serializedString;
        }

        public static string Serialize(object instance, string filePath, bool useSerializerExtension = true)
        {
            if (instance == null)
                throw new ArgumentNullException("instance");
            if (filePath == null)
                throw new ArgumentNullException("filePath");
            string serializedString = Serialize(instance, useSerializerExtension);
            WriteText(filePath, serializedString);
            return serializedString;
        }

        public static T Deserialize<T>(string filePath)
        {
            if (filePath == null)
                throw new ArgumentNullException("filePath");
            string serializedString = ReadText(filePath);
            if (serializedString.HasValue())
            {
                return DeserializeString<T>(serializedString);
            }
            else
                return default(T);
        }

        public static T DeserializeString<T>(string serializedString)
        {
            try
            {
                return JSON.Instance.ToObject<T>(serializedString);
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                return default(T);
            }
        }

        public static object Deserialize(string filePath)
        {
            if (filePath == null)
                throw new ArgumentNullException("filePath");
            string serializedString = ReadText(filePath);
            if (serializedString.HasValue())
            {
                return DeserializeString(serializedString);
            }
            else
                return null;
        }

        /// <summary>
        /// Needs useSerializerExtension =true when serialized
        /// </summary>
        /// <param name="serializedString"></param>
        /// <returns></returns>
        public static object DeserializeString(string serializedString)
        {
            try
            {
                return JSON.Instance.ToObject(serializedString);
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                return null;
            }
        }

        public static void VerifyDirectory(string directory)
        {
            if (directory == null)
                throw new ArgumentNullException("directory");
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);
        }

        public static void FileRename(string source, string destination)
        {
            if (File.Exists(source))
            {
                File.Move(source, destination);
                File.Delete(source);
            }
            else
                Log.Warning(Msg.PathNotFound(source));
        }

        public static string GetMb(long bytes)
        {
            double megas = (double)bytes / BIN10 / BIN10;
            return megas.ToString("N2") + " Mb.";
        }

        public static string GetKb(long bytes)
        {
            double megas = (double)bytes / BIN10;
            return megas.ToString("N0") + " Kb.";
        }

        public static string GetClassName(object instance)
        {
            Type type = instance.GetType();
            return type.Name;
        }

        public static void SetValue(object target, string propertyName, object value)
        {
            var propertyInfo = GetPropertyInfo(target, propertyName);
            if (propertyInfo != null)
                propertyInfo.SetValue(target, value, null);
        }

        public static object GetValue(object target, string propertyName)
        {
            var propertyInfo = GetPropertyInfo(target, propertyName);
            if (propertyInfo != null)
                return propertyInfo.GetValue(target, null);
            else
                return null;
        }

        public static PropertyInfo GetPropertyInfo(object target, string propertyName)
        {
            Type type = target.GetType();
            return GetPropertyInfo(type, propertyName);
        }

        public static PropertyInfo GetPropertyInfo(Type target, string propertyName)
        {
            string key = target.FullName + "." + propertyName;
            var propertyInfo = (PropertyInfo)Cache.Container.Get(key);
            if (propertyInfo == null)
            {
                propertyInfo = target.GetProperty(propertyName);
                Cache.Container.Set(key, propertyInfo);
            }
            return propertyInfo;
        }

        public static object GetPrivateValue(object target, string fieldName)
        {
            var fieldInfo = GetFieldInfo(target, fieldName);
            if (fieldInfo != null)
                return fieldInfo.GetValue(target);
            else
                return null;
        }

        public static FieldInfo GetFieldInfo(object target, string fieldName)
        {
            Type type = target.GetType();
            return GetFieldInfo(type, fieldName);
        }

        public static FieldInfo GetFieldInfo(Type target, string fieldName)
        {
            string key = target.FullName + "." + fieldName;
            var fieldInfo = (FieldInfo)Cache.Container.Get(key);
            if (fieldInfo == null)
            {
                fieldInfo = target.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
                Cache.Container.Set(key, fieldInfo);
            }
            return fieldInfo;
        }

        public static void InvokeMethod(object target, string methodName, object[] parameters)
        {
            var methodInfo = GetMethodInfo(target, methodName);
            methodInfo.Invoke(target, parameters);
        }

        public static MethodInfo GetMethodInfo(object target, string methodName)
        {
            Type type = target.GetType();
            return GetMethodInfo(type, methodName);
        }

        public static MethodInfo GetMethodInfo(Type pocoType, string methodName)
        {
            string key = pocoType.FullName + "." + methodName;
            var methodInfo = (MethodInfo)Cache.Container.Get(key);
            if (methodInfo == null)
            {
                methodInfo = pocoType.GetMethod(methodName);
                Cache.Container.Set(key, methodInfo);
            }
            return methodInfo;
        }

        /// <summary>
        /// Sends the message by email
        /// </summary>
        /// <param name="subject"></param>
        /// <param name="body"></param>
        /// <param name="recipientAddress"></param>
        /// <param name="recipientName"></param>
        /// <returns></returns>
        public static void SendEmailMessage(string subject, string body, string recipientAddress, string recipientName, bool isBodyHtml=false)
        {
            var fromAddress = new MailAddress(Config.EmailAddress, Config.ApplicationName);
            var toAddress = new MailAddress(recipientAddress, recipientName);
            var message = new MailMessage(fromAddress, toAddress)
            {
                Subject = subject,
                Body = body,
                IsBodyHtml = isBodyHtml
            };
            SendEmailMessage(message);
        }


        /// <summary>
        /// Sends the message by email
        /// </summary>
        /// <param name="subject"></param>
        /// <param name="body"></param>
        /// <param name="recipientAddress"></param>
        /// <param name="recipientName"></param>
        /// <returns></returns>
        public static void SendEmailMessage(MailMessage message)
        {
            var smtp = new SmtpClient
            {
                Host = Config.SmtpAddress,
                Port = Config.SmtpPortAddress,
                EnableSsl = false,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                UseDefaultCredentials = false,
                Credentials = new NetworkCredential(message.From.Address, Config.SmtpPassword)
            };
            {
                try
                {
                    smtp.Send(message);
                }
                catch ( Exception e )
                {
                    Log.Error(
                        new ApplicationException(
                            string.Format("Error sending From {0} to {1} through {2}:{3} pass:{4}", message.From.Address, message.To[0].Address, Config.SmtpAddress,
                                Config.SmtpPortAddress, Config.SmtpPassword), e));
                }
            }
        }
    }
}